
package com.hlkj.pay.gateway.profit.impl;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.collection.CollectionUtils;
import com.hlkj.framework.extension.executor.ExtensionExecutor;
import com.hlkj.framework.extension.scenario.BizScenario;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantProfitOrderExtPt;
import com.hlkj.pay.app.merchant.ext.dto.profit.ChannelProfitOrderContext;
import com.hlkj.pay.app.merchant.ext.dto.profit.ChannelProfitOrderQueryContext;
import com.hlkj.pay.dto.order.PayOrderFundDto;
import com.hlkj.pay.dto.order.ProfitOrderResultDto;
import com.hlkj.pay.dto.order.RefundOrderFundDto;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.gateway.profit.IChannelProfitOrderGateway;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAppChannelDO;
import com.hlkj.pay.infrastructure.model.order.OrderFundDO;
import com.hlkj.pay.infrastructure.model.order.OrderFundDetailDO;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.pay.PayChannelRequestDO;
import com.hlkj.pay.service.merchant.IMerchantApplicationService;
import com.hlkj.pay.service.pay.IPayChannelService;
import com.hlkj.pay.service.pay.IPayWayService;
import com.hlkj.pay.service.pay.IProviderService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author HlpayTeam
 * @date 2025/01/06 17:38
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ChannelProfitOrderGatewayImpl implements IChannelProfitOrderGateway {

    @Value("${hlkj.pay.barrier:false}")
    private boolean barrier;

    @Value("${hlkj.pay.test:false}")
    private boolean test;

    @Value("${hlkj.domain}")
    private String domain;

    private final ExtensionExecutor extensionExecutor;

    private final IMerchantApplicationService merchantApplicationService;

    private final IProviderService providerService;

    private final IPayChannelService payChannelService;

    private final IPayWayService payWayService;

    @Override
    public boolean extensionExist(String channelCode) {
        return extensionExecutor.executeExist(ChannelMerchantProfitOrderExtPt.class, BizScenario.valueOf(channelCode));
    }

    @Override
    public ProfitOrderResultDto submitProfitOrder(PayOrderDO payOrderDO, List<PayOrderFundDto> payOrderFundDtoList) {
        ProfitOrderResultDto payOrderChannelResultDto = new ProfitOrderResultDto();
        if (barrier) {
            log.info("测试环境提交分账订单直接返回成功 payOrderNo:{}", payOrderDO.getPayOrderNo());
            payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FUNDING);
            return payOrderChannelResultDto;
        }
        if(!extensionExist(payOrderDO.getChannelCode())){
            payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FUNDING);
            return payOrderChannelResultDto;
        }
        MerchantAppChannelDO merchantAppChannelDO = merchantApplicationService.queryAppChannelDetail(payOrderDO.getMchChannelCode());
        ChannelProfitOrderContext channelProfitOrderContext = new ChannelProfitOrderContext();
        channelProfitOrderContext.setMerchantAppChannelDO(merchantAppChannelDO);
        channelProfitOrderContext.setPayOrderDO(payOrderDO);
        channelProfitOrderContext.setProviderChannelConfig(providerService.queryConfig(merchantAppChannelDO.getProviderCode(), merchantAppChannelDO.getChannelCode()));
        channelProfitOrderContext.setPayOrderFundDtoList(payOrderFundDtoList);
        channelProfitOrderContext.setTest(test);
        channelProfitOrderContext.setPayWayDO(payWayService.queryByCode(payOrderDO.getPayWayCode()));
        extensionExecutor.executeVoid(ChannelMerchantProfitOrderExtPt.class, BizScenario.valueOf(merchantAppChannelDO.getChannelCode()),
                ex -> ex.submitOrderProfit(channelProfitOrderContext));
        PayChannelRequestDO payChannelRequestDO = channelProfitOrderContext.getPayChannelRequest();
        if (payChannelRequestDO != null) {
            payChannelRequestDO.setRelationId(payOrderFundDtoList.get(0).getBatchNo());
            payChannelRequestDO.setSn(payChannelRequestDO.getSn());
            payChannelRequestDO.setChannelCode(payOrderDO.getChannelCode());
            payChannelRequestDO.setProviderCode(payOrderDO.getProviderCode());
            payChannelService.saveRequest(payChannelRequestDO, PayEnums.CHANNEL_REQUEST_TYPE.PROFIT_ORDER);
        }
        if (!channelProfitOrderContext.isProcessStatus()) {
            payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL);
            payOrderFundDtoList.stream().forEach(payOrderFundDto -> {
                payOrderFundDto.setFailReason(channelProfitOrderContext.getRemark());
                payOrderFundDto.setFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL);
            });
        }
        else {
            List<PayOrderFundDto> failPayOrderFundList = payOrderFundDtoList.stream()
                    .filter(payOrderFundDto -> PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL.getCode().equals(payOrderFundDto.getFundStatus().getCode())).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(failPayOrderFundList)) {
                payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FUNDING);
            }
            else {
                if (failPayOrderFundList.size() == payOrderFundDtoList.size()) {
                    payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL);
                }
                else {
                    payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FUNDING);
                }
            }
        }
        return payOrderChannelResultDto;
    }

    @Override
    public ProfitOrderResultDto queryProfitOrder(PayOrderDO payOrderDO, List<OrderFundDetailDO> payOrderFundList) {
        if (barrier) {
            log.info("测试环境查询分账订单直接返回成功 payOrderNo:{}", payOrderDO.getPayOrderNo());
            ProfitOrderResultDto payOrderChannelResultDto = new ProfitOrderResultDto();
            payOrderFundList.stream().forEach(payOrderFundDO -> {
                payOrderFundDO.setFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FUNDING.getCode());
            });
            payOrderChannelResultDto.setPayOrderFundList(payOrderFundList);
            return payOrderChannelResultDto;
        }
        ProfitOrderResultDto payOrderChannelResultDto = new ProfitOrderResultDto();
        if(!extensionExist(payOrderDO.getChannelCode())){
            payOrderChannelResultDto.setPayOrderFundList(payOrderFundList);
            return payOrderChannelResultDto;
        }
        MerchantAppChannelDO merchantAppChannelDO = merchantApplicationService.queryAppChannelDetail(payOrderDO.getMchChannelCode());
        ChannelProfitOrderQueryContext merchantPayOrderContext = new ChannelProfitOrderQueryContext();
        merchantPayOrderContext.setMerchantAppChannelDO(merchantAppChannelDO);
        merchantPayOrderContext.setPayOrderDO(payOrderDO);
        merchantPayOrderContext.setPayWayDO(payWayService.queryByCode(payOrderDO.getPayWayCode()));
        merchantPayOrderContext.setProviderChannelConfig(providerService.queryConfig(merchantAppChannelDO.getProviderCode(), merchantAppChannelDO.getChannelCode()));
        merchantPayOrderContext.setPayOrderFunds(payOrderFundList);
        merchantPayOrderContext.setTest(test);
        extensionExecutor.executeVoid(ChannelMerchantProfitOrderExtPt.class, BizScenario.valueOf(merchantAppChannelDO.getChannelCode()),
                ex -> ex.queryOrderProfit(merchantPayOrderContext));
        payOrderChannelResultDto.setPayOrderFundList(merchantPayOrderContext.getPayOrderFunds());
        return payOrderChannelResultDto;
    }

    @Override
    public ProfitOrderResultDto submitProfitRefundOrder(PayOrderDO payOrderDO, List<RefundOrderFundDto> refundOrderFundList) {
        if (barrier) {
            log.info("测试环境提交退款分账订单直接返回成功 payOrderNo:{}", payOrderDO.getPayOrderNo());
            ProfitOrderResultDto payOrderChannelResultDto = new ProfitOrderResultDto();
            payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FUNDING);
            return payOrderChannelResultDto;
        }
        MerchantAppChannelDO merchantAppChannelDO = merchantApplicationService.queryAppChannelDetail(payOrderDO.getMchChannelCode());
        ChannelProfitOrderContext channelProfitOrderContext = new ChannelProfitOrderContext();
        channelProfitOrderContext.setMerchantAppChannelDO(merchantAppChannelDO);
        channelProfitOrderContext.setPayOrderDO(payOrderDO);
        channelProfitOrderContext.setPayWayDO(payWayService.queryByCode(payOrderDO.getPayWayCode()));
        channelProfitOrderContext.setProviderChannelConfig(providerService.queryConfig(merchantAppChannelDO.getProviderCode(), merchantAppChannelDO.getChannelCode()));
        channelProfitOrderContext.setRefundOrderFundDtoList(refundOrderFundList);
        channelProfitOrderContext.setTest(test);
        extensionExecutor.executeVoid(ChannelMerchantProfitOrderExtPt.class, BizScenario.valueOf(merchantAppChannelDO.getChannelCode()),
                ex -> ex.submitRefundOrderProfit(channelProfitOrderContext));
        PayChannelRequestDO payChannelRequestDO = channelProfitOrderContext.getPayChannelRequest();
        if (payChannelRequestDO != null) {
            payChannelRequestDO.setRelationId(refundOrderFundList.get(0).getBatchNo());
            payChannelRequestDO.setSn(payChannelRequestDO.getSn());
            payChannelRequestDO.setChannelCode(payOrderDO.getChannelCode());
            payChannelRequestDO.setProviderCode(payOrderDO.getProviderCode());
            payChannelService.saveRequest(payChannelRequestDO, PayEnums.CHANNEL_REQUEST_TYPE.PROFIT_ORDER);
        }
        ProfitOrderResultDto payOrderChannelResultDto = new ProfitOrderResultDto();
        if (!channelProfitOrderContext.isProcessStatus()) {
            payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL);
            refundOrderFundList.stream().forEach(payOrderFundDto -> {
                payOrderFundDto.setFailReason(channelProfitOrderContext.getRemark());
            });
        }
        else {
            List<PayOrderFundDto> failPayOrderFundList = refundOrderFundList.stream()
                    .filter(payOrderFundDto -> PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL.getCode().equals(payOrderFundDto.getFundStatus().getCode())).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(failPayOrderFundList)) {
                payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.REFUND);
            }
            else {
                if (failPayOrderFundList.size() == refundOrderFundList.size()) {
                    payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.REFUND_FAIL);
                }
                else {
                    payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.REFUNDING);
                }
            }
        }
        return payOrderChannelResultDto;
    }

    @Override
    public ProfitOrderResultDto queryProfitRefundOrder(PayOrderDO payOrderDO, List<OrderFundDetailDO> payOrderFundList) {
        if (barrier) {
            log.info("测试环境查询退款分账订单直接返回成功 payOrderNo:{}", payOrderDO.getPayOrderNo());
            ProfitOrderResultDto payOrderChannelResultDto = new ProfitOrderResultDto();
            payOrderFundList.stream().forEach(payOrderFundDO -> {
                payOrderFundDO.setFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FUNDING.getCode());
            });
            payOrderChannelResultDto.setRefundOrderFundList(payOrderFundList);
            return payOrderChannelResultDto;
        }
        ProfitOrderResultDto payOrderChannelResultDto = new ProfitOrderResultDto();
        if(!extensionExist(payOrderDO.getChannelCode())){
            payOrderChannelResultDto.setRefundOrderFundList(payOrderFundList);
            return payOrderChannelResultDto;
        }
        MerchantAppChannelDO merchantAppChannelDO = merchantApplicationService.queryAppChannelDetail(payOrderDO.getMchChannelCode());
        ChannelProfitOrderQueryContext channelProfitOrderQueryContext = new ChannelProfitOrderQueryContext();
        channelProfitOrderQueryContext.setMerchantAppChannelDO(merchantAppChannelDO);
        channelProfitOrderQueryContext.setPayOrderDO(payOrderDO);
        channelProfitOrderQueryContext.setPayWayDO(payWayService.queryByCode(payOrderDO.getPayWayCode()));
        channelProfitOrderQueryContext.setProviderChannelConfig(providerService.queryConfig(merchantAppChannelDO.getProviderCode(), merchantAppChannelDO.getChannelCode()));
        channelProfitOrderQueryContext.setRefundOrderFunds(payOrderFundList);
        channelProfitOrderQueryContext.setTest(test);
        extensionExecutor.executeVoid(ChannelMerchantProfitOrderExtPt.class, BizScenario.valueOf(merchantAppChannelDO.getChannelCode()),
                ex -> ex.queryRefundOrderProfit(channelProfitOrderQueryContext));
        payOrderChannelResultDto.setRefundOrderFundList(channelProfitOrderQueryContext.getRefundOrderFunds());
        return payOrderChannelResultDto;
    }

    @Override
    public ProfitOrderResultDto finishAcctOrder(PayOrderDO payOrderDO, OrderFundDO payOrderFund) {
        if (barrier) {
            log.info("测试环境结束分账订单直接返回成功 payOrderNo:{}", payOrderDO.getPayOrderNo());
            ProfitOrderResultDto payOrderChannelResultDto = new ProfitOrderResultDto();
            payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.CLOSE);
            return payOrderChannelResultDto;
        }
        ProfitOrderResultDto payOrderChannelResultDto = new ProfitOrderResultDto();
        if(!extensionExist(payOrderDO.getChannelCode())){
            payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.CLOSE);
            return payOrderChannelResultDto;
        }
        MerchantAppChannelDO merchantAppChannelDO = merchantApplicationService.queryAppChannelDetail(payOrderDO.getMchChannelCode());
        ChannelProfitOrderContext channelProfitOrderContext = new ChannelProfitOrderContext();
        channelProfitOrderContext.setMerchantAppChannelDO(merchantAppChannelDO);
        channelProfitOrderContext.setPayOrderDO(payOrderDO);
        channelProfitOrderContext.setProviderChannelConfig(providerService.queryConfig(merchantAppChannelDO.getProviderCode(), merchantAppChannelDO.getChannelCode()));
        channelProfitOrderContext.setOrderFundDO(payOrderFund);
        channelProfitOrderContext.setTest(test);
        extensionExecutor.executeVoid(ChannelMerchantProfitOrderExtPt.class, BizScenario.valueOf(merchantAppChannelDO.getChannelCode()),
                ex -> ex.finishAcctOrder(channelProfitOrderContext));

        if(!channelProfitOrderContext.isProcessStatus()){
            payOrderChannelResultDto.setRemark(channelProfitOrderContext.getRemark());
        }

        payOrderChannelResultDto.setPayOrderFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.CLOSE);
        return payOrderChannelResultDto;
    }

    @Override
    public CommonResult<String> acctStatusParse(HttpServletRequest request, ChannelProfitOrderContext channelProfitOrderContext) {
        return extensionExecutor.execute(ChannelMerchantProfitOrderExtPt.class, BizScenario.valueOf(channelProfitOrderContext.getProviderChannelConfig().getChannelCode()),
                ex -> ex.acctStatusParse(request,channelProfitOrderContext));

    }

    @Override
    public CommonResult<String> settleConfirm(PayOrderDO payOrderDO) {
        if(!extensionExist(payOrderDO.getChannelCode())){
            return CommonResult.error(0,payOrderDO.getChannelCode()+"：通道不存在");
        }
        MerchantAppChannelDO merchantAppChannelDO = merchantApplicationService.queryAppChannelDetail(payOrderDO.getMchChannelCode());
        ChannelProfitOrderContext channelProfitOrderContext = new ChannelProfitOrderContext();
        channelProfitOrderContext.setMerchantAppChannelDO(merchantAppChannelDO);
        channelProfitOrderContext.setPayOrderDO(payOrderDO);
        channelProfitOrderContext.setProviderChannelConfig(providerService.queryConfig(merchantAppChannelDO.getProviderCode(), merchantAppChannelDO.getChannelCode()));
        channelProfitOrderContext.setTest(test);
        extensionExecutor.executeVoid(ChannelMerchantProfitOrderExtPt.class, BizScenario.valueOf(merchantAppChannelDO.getChannelCode()),
                ex -> ex.settleConfirm(channelProfitOrderContext));

        if(!channelProfitOrderContext.isProcessStatus()){
            return CommonResult.error(0,channelProfitOrderContext.getRemark());
        }
        return CommonResult.success();
    }
}
